﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace ChatBox.Commands
{
  /// <summary>
  /// Defines a command that calls the given methods with the given parameter.
  /// To be used for commands in WPF. The CanExecuteChanged event must be called
  /// manually to refresh the command.
  /// </summary>
  /// <typeparam name="T">Type of command parameter to use.</typeparam>
  public class SimpleCommand<T> : ICommand
  {
    #region Constructors

    /// <summary>
    /// Initializes a SimpleCommand that will call the given action Execute is called. The <see cref="M:CanExecute"/> method will always return true.
    /// </summary>
    /// <param name="executeAction">Action to invoke when Execute is called.</param>
    /// <exception cref="System.ArgumentNullException">executeAction is null.</exception>
    public SimpleCommand( Action<T> executeAction )
      : this( executeAction, x => true )
    {
    }

    /// <summary>
    /// Initializes a SimpleCommand that will call the given action and predicate when Execute and CanExecute are called.
    /// </summary>
    /// <param name="executeAction">Action to invoke when Execute is called.</param>
    /// <param name="canExecuteAction">Predicate to invoke when CanExecute is called.</param>
    /// <exception cref="System.ArgumentNullException">executeAction or canExecuteAction is null.</exception>
    public SimpleCommand( Action<T> executeAction, Predicate<T> canExecuteAction )
    {
      if ( executeAction == null )
      {
        throw new ArgumentNullException( "executeAction" );
      }

      if ( canExecuteAction == null )
      {
        throw new ArgumentNullException( "canExecuteAction" );
      }

      this._executeAction = executeAction;
      this._canExecuteAction = canExecuteAction;
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Raises the <see cref="CanExecuteChanged"/> event.
    /// </summary>
    public virtual void OnCanExecuteActionChanged()
    {
      EventHandler handler = this.CanExecuteChanged;

      if ( handler != null )
      {
        handler( this, EventArgs.Empty );
      }
    }

    #endregion

    #region Private Variables

    private readonly Action<T> _executeAction;

    private readonly Predicate<T> _canExecuteAction;

    #endregion

    #region ICommand Implementation

    /// <summary>
    /// Calls the give predicate when the simple command was created. If no predicate was given, always returns true.
    /// </summary>
    /// <param name="parameter">Data used by the command. If the command does not require data to be passed this object can be set to null.</param>
    /// <returns>True if the predicate returns true or if no predicate was given; otherwise, false.</returns>
    public bool CanExecute( object parameter )
    {
      return this._canExecuteAction( parameter is T ? ( T )parameter : default( T ) );
    }

    /// <summary>
    /// Occurs when changes occur that affect whether or not the command should execute.
    /// </summary>
    /// <remarks>
    /// Normally, a command source calls CanExecute on the command when this event occurs.
    /// Normally, if the command cannot execute, the command source disables itself.
    /// </remarks>
    public event EventHandler CanExecuteChanged;

    /// <summary>
    /// Calls the action that was given when the simple command was created.
    /// </summary>
    /// <param name="parameter"></param>
    public void Execute( object parameter )
    {
      this._executeAction( parameter is T ? ( T )parameter : default( T ) );
    }

    #endregion
  }

  /// <summary>
  /// Defines a command that calls the given methods without a parameter.
  /// To be used for commands in WPF. The CanExecuteChanged event must be called
  /// manually to refresh the command.
  /// </summary>
  public class SimpleCommand : ICommand
  {
    #region Constructors

    /// <summary>
    /// Initializes a SimpleCommand that will call the given action Execute is called. The <see cref="M:CanExecute"/> method will always return true.
    /// </summary>
    /// <param name="executeAction">Action to invoke when Execute is called.</param>
    /// <exception cref="System.ArgumentNullException">executeAction is null.</exception>
    public SimpleCommand( Action executeAction )
      : this( executeAction, () => true )
    {
    }

    /// <summary>
    /// Initializes a SimpleCommand that will call the given action and predicate when Execute and CanExecute are called.
    /// </summary>
    /// <param name="executeAction">Action to invoke when Execute is called.</param>
    /// <param name="canExecuteAction">Predicate to invoke when CanExecute is called.</param>
    /// <exception cref="System.ArgumentNullException">executeAction or canExecuteAction is null.</exception>
    public SimpleCommand( Action executeAction, Func<Boolean> canExecuteAction )
    {
      if ( executeAction == null )
      {
        throw new ArgumentNullException( "executeAction" );
      }

      if ( canExecuteAction == null )
      {
        throw new ArgumentNullException( "canExecuteAction" );
      }

      this._executeAction = executeAction;
      this._canExecuteAction = canExecuteAction;
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Raises the <see cref="CanExecuteChanged"/> event.
    /// </summary>
    public virtual void OnCanExecuteActionChanged()
    {
      EventHandler handler = this.CanExecuteChanged;

      if ( handler != null )
      {
        handler( this, EventArgs.Empty );
      }
    }

    #endregion

    #region Private Variables

    private readonly Action _executeAction;

    private readonly Func<Boolean> _canExecuteAction;

    #endregion

    #region ICommand Implementation

    /// <summary>
    /// Calls the give predicate when the simple command was created. If no predicate was given, always returns true.
    /// </summary>
    /// <param name="parameter">Data used by the command. If the command does not require data to be passed this object can be set to null.</param>
    /// <returns>True if the predicate returns true or if no predicate was given; otherwise, false.</returns>
    public bool CanExecute( object parameter )
    {
      return this._canExecuteAction();
    }

    /// <summary>
    /// Occurs when changes occur that affect whether or not the command should execute.
    /// </summary>
    /// <remarks>
    /// Normally, a command source calls CanExecute on the command when this event occurs.
    /// Normally, if the command cannot execute, the command source disables itself.
    /// </remarks>
    public event EventHandler CanExecuteChanged;

    /// <summary>
    /// Calls the action that was given when the simple command was created.
    /// </summary>
    /// <param name="parameter"></param>
    public void Execute( object parameter )
    {
      this._executeAction();
    }

    #endregion
  }
}
